/**
 * Xy Common Tools
 */
type DateFormatToken =
  | 'YYYY' | 'YY' | 'MM' | 'DD'
  | 'HH' | 'hh' | 'mm' | 'ss'
  | 'A' | 'a';

interface FormatConfig {
  dateSeparator?: string;
  timeSeparator?: string;
  hour12?: boolean;
  amText?: string;
  pmText?: string;
}

interface LogConfig {
  title?: string;
  styles?: string;
  badgeColor?: string;
}

class XyTools {
  private static readonly CN_NUMBERS = ["〇", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
  private static readonly DEFAULT_LOG_STYLE = "padding: 1px 6px; border-radius: 5px; color: #fff;";

  /**
   * 高级日期格式化
   * @param date 日期对象或时间戳
   * @param formatStr 格式字符串，例如：YYYY-MM-DD HH:mm:ss
   * @param config 格式化配置
   */
  static formatDate(
    date: Date | number,
    formatStr: string = 'YYYY-MM-DD HH:mm:ss',
    config: FormatConfig = {}
  ): string {
    const d = new Date(date);
    const {
      hour12 = false,
      dateSeparator = '-',
      timeSeparator = ':',
      amText = '上午',
      pmText = '下午'
    } = config;

    const pad = (n: number) => n.toString().padStart(2, '0');

    const year = d.getFullYear();
    const month = d.getMonth() + 1;
    const day = d.getDate();
    let hours = d.getHours();
    const minutes = d.getMinutes();
    const seconds = d.getSeconds();

    let period = '';
    if (hour12) {
      period = hours >= 12 ? pmText : amText;
      hours = hours % 12 || 12; // 将 0 点转换为 12
    }

    const tokens: Record<DateFormatToken, string> = {
      YYYY: year.toString(),
      YY: year.toString().slice(-2),
      MM: pad(month),
      DD: pad(day),
      HH: pad(hours),
      hh: pad(hours),
      mm: pad(minutes),
      ss: pad(seconds),
      A: period,
      a: period.toLowerCase()
    };

    return formatStr.replace(/(YYYY|YY|MM|DD|HH|hh|mm|ss|A|a)/g, (match) => {
      return tokens[match as DateFormatToken] || match;
    })
      .replace(/-/g, dateSeparator)
      .replace(/:/g, timeSeparator);
  }

  /**
   * 中文日期字符串
   */
  static chineseDateString(date: Date): string {
    const cnYear = date.getFullYear().toString()
      .split('')
      .map(n => this.CN_NUMBERS[parseInt(n)]).join('');

    const month = date.getMonth() + 1;
    const day = date.getDate();

    const cnMonth = month < 10
      ? this.CN_NUMBERS[month]
      : `十${month > 10 ? this.CN_NUMBERS[month - 10] : ''}`;

    const cnDay = day < 10
      ? this.CN_NUMBERS[day]
      : day < 20
        ? `十${this.CN_NUMBERS[day - 10]}`
        : `二十${this.CN_NUMBERS[day - 20]}`;

    return `${cnYear}年${cnMonth}月${cnDay}日`;
  }

  /**
   * 存储操作
   */
  static storage = {
    set<T>(key: string, value: T, persistent: boolean = true): boolean {
      try {
        const storage = persistent ? localStorage : sessionStorage;
        storage.setItem(key, JSON.stringify(value));
        return true;
      } catch (error) {
        console.error('Storage quota exceeded!');
        return false;
      }
    },

    get<T>(key: string, persistent: boolean = true): T | null {
      const storage = persistent ? localStorage : sessionStorage;
      const value = storage.getItem(key);
      return value ? JSON.parse(value) : null;
    },

    remove(key: string, persistent: boolean = true): boolean {
      const storage = persistent ? localStorage : sessionStorage;
      storage.removeItem(key);
      return true;
    }
  }

  /**
  * 日志方法
  */
  static log(data: any): void;
  static log(data: any, config: LogConfig): void;
  static log(message: string, data: any): void;
  static log(message: string, data: any, config: LogConfig): void;
  static log(arg1: any, arg2?: any, arg3?: LogConfig): void {
    let message = '';
    let data: any;
    let config: LogConfig = {};

    if (typeof arg1 === 'string') {
      message = arg1;
      data = arg2;
      config = arg3 || {};
    } else {
      // data 或 data + config
      data = arg1;
      config = arg2 || {};
    }

    this._print('log', '#527ce8', message, data, config);
  }

  static warn(data: any): void;
  static warn(data: any, config: LogConfig): void;
  static warn(message: string, data: any): void;
  static warn(message: string, data: any, config: LogConfig): void;
  static warn(arg1: any, arg2?: any, arg3?: LogConfig): void {
    let message = '';
    let data: any;
    let config: LogConfig = {};

    if (typeof arg1 === 'string') {
      message = arg1;
      data = arg2;
      config = arg3 || {};
    } else {
      data = arg1;
      config = arg2 || {};
    }

    this._print('warn', '#f80', message, data, config);
  }

  static error(data: any): void;
  static error(data: any, config: LogConfig): void;
  static error(message: string, data: any): void;
  static error(message: string, data: any, config: LogConfig): void;
  static error(arg1: any, arg2?: any, arg3?: LogConfig): void {
    let message = '';
    let data: any;
    let config: LogConfig = {};

    if (typeof arg1 === 'string') {
      message = arg1;
      data = arg2;
      config = arg3 || {};
    } else {
      data = arg1;
      config = arg2 || {};
    }

    this._print('error', '#f00', message, data, config);
  }

  private static _print(
    method: 'log' | 'warn' | 'error',
    color: string,
    message: string,
    data?: any,
    config: LogConfig = {}
  ) {
    const { title = 'xy', styles = '' } = config;
    const css = `${this.DEFAULT_LOG_STYLE} background-color: ${color}; ${styles}`;

    let logArgs;

    if (message) {
      logArgs = data !== undefined
        ? [`%c${title}`, css, message, data]
        : [`%c${title}`, css, message];
    } else {
      logArgs = data !== undefined
        ? [`%c${title}`, css, data]
        : [`%c${title}`, css];
    }



    console[method](...logArgs);
  }

  /**
   * 生成随机ID
   */
  static randomId(length: number = 16): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const crypto = window.crypto || (window as any).msCrypto;

    if (crypto?.getRandomValues) {
      const values = new Uint32Array(length);
      crypto.getRandomValues(values);
      return Array.from(values)
        .map(n => chars[n % chars.length])
        .join('')
        .slice(0, length);
    }

    return Array.from({ length }, () =>
      chars[Math.floor(Math.random() * chars.length)]
    ).join('');
  }

  /**
   * 图片压缩
   */
  static async compressImage(
    file: File,
    options: {
      maxWidth?: number;
      maxHeight?: number;
      quality?: number;
    } = {}
  ): Promise<File> {
    const { maxWidth = 1600, maxHeight = 900, quality = 0.8 } = options;

    return new Promise((resolve, reject) => {
      const img = new Image();
      img.src = URL.createObjectURL(file);

      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d')!;

        let width = img.width;
        let height = img.height;

        if (width > maxWidth || height > maxHeight) {
          const ratio = Math.min(maxWidth / width, maxHeight / height);
          width *= ratio;
          height *= ratio;
        }

        canvas.width = width;
        canvas.height = height;
        ctx.drawImage(img, 0, 0, width, height);

        canvas.toBlob(
          blob => {
            if (!blob) return reject(new Error('Compression failed'));
            resolve(new File([blob], file.name, { type: 'image/jpeg' }));
          },
          'image/jpeg',
          quality
        );
      };

      img.onerror = reject;
    });
  }

  /**
   * URL参数解析
   */
  static parseQuery(query: string = window.location.search): Record<string, string> {
    return Object.fromEntries(new URLSearchParams(query) as any);
  }

  /**
    * 防抖函数
    */
  static debounce<T extends (...args: any[]) => any>(
    func: T,
    delay: number
  ): (...args: Parameters<T>) => void {
    let timeoutId: ReturnType<typeof setTimeout>;
    return function (this: any, ...args: Parameters<T>) {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => func.apply(this, args), delay);
    };
  }

  /**
   * 节流函数
   */
  static throttle<T extends (...args: any[]) => any>(
    func: T,
    delay: number
  ): (...args: Parameters<T>) => void {
    let lastCall = 0;
    return function (this: any, ...args: Parameters<T>) {
      const now = Date.now();
      if (now - lastCall >= delay) {
        lastCall = now;
        func.apply(this, args);
      }
    };
  }

  /**
   * 金额格式化
   */
  static formatCurrency(amount: number, options: Intl.NumberFormatOptions = {}): string {
    const defaults: Intl.NumberFormatOptions = {
      style: 'currency',
      currency: 'CNY',
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
      currencyDisplay: options.currency === 'USD' ? 'narrowSymbol' : 'symbol'
    };
    return new Intl.NumberFormat('zh-CN', { ...defaults, ...options }).format(amount);
  }

  /**
   * 事件处理
   */
  static stopEvent(e: Event) {
    e.preventDefault();
    e.stopPropagation();
  }

  /**
   * 空值检查
   */
  static isEmpty(value: unknown): boolean {
    if (typeof value === 'string') return value.trim() === '';
    if (value === null || value === undefined) return true;
    if (Array.isArray(value)) return value.length === 0;
    if (typeof value === 'object') return Object.keys(value).length === 0;
    return false;
  }
}

export default XyTools;